﻿using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace ExcelExpoter.Editor
{
    public static class ExporterPath
    {
        public static readonly string KeyExcelFileSavePath = "Key_ExcelFileSavePath";
        public static readonly string KeyLuaDataSavePath = "Key_LuaDataSavePath";
        public static readonly string KeyByteDataSavePath = "Key_ByteDataSavePath";
        public static readonly string KeyCsTemplateSavePath = "Key_CsTemplateSavePath";
        
        public static readonly string ExporterLuaHashConfig = "ExportLuaSetting";
        public static readonly string ExporterCsHashConfig = "ExportCsSetting";
    }

    public class ExcelItem
    {
        public bool Select;
        public string Name;
        public string FullPath;
        public int Hash;
        public ExcelType Type;
    }

    public enum ExcelType
    {
        Normal,New,Modify,
    }

    public enum ExporterType
    {
        Lua,Cs,
    }

    public class ExcelExporterWindow : EditorWindow
    {
        private VisualElement m_SelectPanel;
        private VisualElement m_LuaExporter;
        private VisualElement m_CsExporter;
        
        private VisualElement m_ExporterPanel;

        private ExporterType m_ExporterType = ExporterType.Lua;
        private Vector2 m_ScrollPos = Vector2.zero;

        private StringBuilder m_LoadError = new StringBuilder();

        [MenuItem("Tools/ExcelExporter")]
        private static void ShowWindow()
        {
            var window = GetWindow<ExcelExporterWindow>();
            window.titleContent = new GUIContent("ExcelExporter", ExcelExporterStyle.MainWindowIcon);
            window.minSize = ExcelExporterStyle.MainWindowMinSize;
            window.Show();
        }

        private void OnEnable()
        {
            this.InitData();
            this.InitGui();
        }

        private void InitData()
        {
            ExporterTool.ExcelPath = PlayerPrefs.GetString(ExporterPath.KeyExcelFileSavePath);
            ExporterTool.LuaDataPath = PlayerPrefs.GetString(ExporterPath.KeyLuaDataSavePath);
            ExporterTool.ByteDataPath = PlayerPrefs.GetString(ExporterPath.KeyByteDataSavePath);
            ExporterTool.CsScriptPath = PlayerPrefs.GetString(ExporterPath.KeyCsTemplateSavePath);
            var config = this.m_ExporterType == ExporterType.Lua
                ? ExporterPath.ExporterLuaHashConfig
                : ExporterPath.ExporterCsHashConfig;
            ExporterTool.HashConfig = Resources.Load<ExporterHash>($"Configs/{config}");
            if (ExporterTool.HashConfig == null)
            {
                ExporterTool.HashConfig = ExporterHash.CreateNew(this.m_ExporterType);
            }
            this.LoadAllExcel();
        }
        
        private void LoadAllExcel()
        {
            this.m_LoadError.Clear();
            ExporterTool.AllExcel.Clear();
            if (string.IsNullOrEmpty(ExporterTool.ExcelPath)) return;
            if (!Directory.Exists(ExporterTool.ExcelPath)) return;
            DirectoryInfo serverDirectoryInfo = new DirectoryInfo(ExporterTool.ExcelPath);
            FileInfo[] serverFileInfos = serverDirectoryInfo.GetFiles("*.xlsx", SearchOption.TopDirectoryOnly);
            
            SHA1 sha1 = SHA1.Create();
            foreach (FileInfo fileInfo in serverFileInfos)
            {
                if (fileInfo.Name.Contains("~$")) continue;
                int hashCode;
                try
                {
                    FileStream stream = File.Open(fileInfo.FullName, FileMode.Open, FileAccess.Read);
                    sha1.Initialize();
                    var hashByte = sha1.ComputeHash(stream);
                    hashCode = Encoding.UTF8.GetString(hashByte).GetHashCode();
                    stream.Close();
                }
                catch (IOException e)
                {
                    this.m_LoadError.AppendLine(e.Message);
                    continue;
                }
                var nameWithoutExtension = fileInfo.Name.Replace(".xlsx", "");
                var item = new ExcelItem() {Name = nameWithoutExtension, Select = false, FullPath = fileInfo.FullName, Hash = hashCode, Type = ExcelType.Normal};
                if (!ExporterTool.HashConfig.IsHashSame(item.Name, hashCode))
                {
                    item.Type = ExcelType.Modify;
                    item.Select = true;
                }
                if (ExporterTool.HashConfig.IsNewExcel(item.Name))
                {
                    item.Type = ExcelType.New;
                    item.Select = true;
                }
                ExporterTool.AllExcel.Add(item);
            }
            if (this.m_LoadError.Length > 0) this.m_LoadError.Insert(0, "以下文件已在其它应用打开! 关闭后重新打开当前编辑器执行导出操作.\n");
        }

        private void InitGui()
        {
            var mainSty = Resources.Load<StyleSheet>("Uss/ExcelExporterMain");
            this.rootVisualElement.styleSheets.Add(mainSty);

            var mainContainer = new VisualElement(){name = "MainContainer"};
            this.rootVisualElement.Add(mainContainer);
            var background = new IMGUIContainer(() =>
            {
                if (Event.current.type != EventType.Repaint) return;
                UnityEditor.Graphs.Styles.graphBackground.Draw(mainContainer.layout, false, false, false, false);
            });
            background.AddToClassList("BackGround");
            mainContainer.Add(background);

            this.m_SelectPanel = new VisualElement();
            this.m_SelectPanel.AddToClassList("SelectPanel");
            mainContainer.Add(this.m_SelectPanel);

            this.m_ExporterPanel = new IMGUIContainer(this.DrawExporterPanel);
            this.m_ExporterPanel.AddToClassList("ExporterPanel");
            mainContainer.Add(this.m_ExporterPanel);
            
            this.DrawSelectPanel();
        }

        private void DrawSelectPanel()
        {
            this.m_LuaExporter = new VisualElement();
            this.m_LuaExporter.AddToClassList("SelectButtonCommon");
            this.m_LuaExporter.RegisterCallback<MouseDownEvent>(this.SelectLuaExporter);
            this.m_LuaExporter.style.backgroundColor = ExcelExporterStyle.Selected;
            Label lab = new Label("Lua导表");
            this.m_LuaExporter.Add(lab);
            this.m_SelectPanel.Add(this.m_LuaExporter);
            
            this.m_CsExporter = new VisualElement();
            this.m_CsExporter.AddToClassList("SelectButtonCommon");
            this.m_CsExporter.RegisterCallback<MouseDownEvent>(this.SelectCsExporter);
            this.m_CsExporter.style.backgroundColor = ExcelExporterStyle.Normal;
            Label lab2 = new Label("CSharp导表");
            this.m_CsExporter.Add(lab2);
            this.m_SelectPanel.Add(this.m_CsExporter);
        }

        private void DrawExporterPanel()
        {
            EditorGUILayout.Space(10);
            GUILayout.BeginHorizontal(ExcelExporterStyle.PathItem);
            ExporterTool.ExcelPath = EditorGUILayout.TextField("Excel表路径", ExporterTool.ExcelPath);
            if (GUILayout.Button("", ExcelExporterStyle.SelectFolder))
            {
                ExporterTool.ExcelPath = EditorUtility.OpenFolderPanel("open res path", ExporterTool.ExcelPath, "");
                if (!string.IsNullOrEmpty(ExporterTool.ExcelPath))
                {
                    PlayerPrefs.SetString(ExporterPath.KeyExcelFileSavePath, ExporterTool.ExcelPath);
                    this.LoadAllExcel();
                }
                ExporterTool.ExcelPath = PlayerPrefs.GetString(ExporterPath.KeyExcelFileSavePath);
            }
            GUILayout.EndHorizontal();

            switch (this.m_ExporterType)
            {
                case ExporterType.Lua:
                    this.DrawLuaItem();
                    break;
                case ExporterType.Cs:
                    this.DrawCsItem();
                    break;
            }
        }

        private void DrawLuaItem()
        {
            GUILayout.BeginHorizontal(ExcelExporterStyle.PathItem);
            ExporterTool.LuaDataPath = EditorGUILayout.TextField("Lua数据保存路径", ExporterTool.LuaDataPath);
            if (GUILayout.Button("", ExcelExporterStyle.SelectFolder))
            {
                ExporterTool.LuaDataPath = EditorUtility.OpenFolderPanel("open res path", ExporterTool.LuaDataPath, "");
                if (!string.IsNullOrEmpty(ExporterTool.LuaDataPath))
                {
                    PlayerPrefs.SetString(ExporterPath.KeyLuaDataSavePath, ExporterTool.LuaDataPath);
                }
                ExporterTool.LuaDataPath = PlayerPrefs.GetString(ExporterPath.KeyLuaDataSavePath);
            }
            GUILayout.EndHorizontal();
            if (string.IsNullOrEmpty(ExporterTool.ExcelPath) || string.IsNullOrEmpty(ExporterTool.LuaDataPath))
            {
                EditorGUILayout.HelpBox("路径未配置.", MessageType.Warning);
                return;
            }
            this.DrawCommonItem();
        }

        private void DrawCsItem()
        {
            GUILayout.BeginHorizontal(ExcelExporterStyle.PathItem);
            ExporterTool.ByteDataPath = EditorGUILayout.TextField("Byte数据保存路径", ExporterTool.ByteDataPath);
            if (GUILayout.Button("", ExcelExporterStyle.SelectFolder))
            {
                ExporterTool.ByteDataPath = EditorUtility.OpenFolderPanel("open res path", ExporterTool.ByteDataPath, "");
                if (!string.IsNullOrEmpty(ExporterTool.ByteDataPath))
                {
                    PlayerPrefs.SetString(ExporterPath.KeyByteDataSavePath, ExporterTool.ByteDataPath);
                }
                ExporterTool.ByteDataPath = PlayerPrefs.GetString(ExporterPath.KeyByteDataSavePath);
            }
            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal(ExcelExporterStyle.PathItem);
            ExporterTool.CsScriptPath = EditorGUILayout.TextField("Cs脚本保存路径", ExporterTool.CsScriptPath);
            if (GUILayout.Button("", ExcelExporterStyle.SelectFolder))
            {
                ExporterTool.CsScriptPath = EditorUtility.OpenFolderPanel("open res path", ExporterTool.CsScriptPath, "");
                if (!string.IsNullOrEmpty(ExporterTool.CsScriptPath))
                {
                    PlayerPrefs.SetString(ExporterPath.KeyCsTemplateSavePath, ExporterTool.CsScriptPath);
                }
                ExporterTool.CsScriptPath = PlayerPrefs.GetString(ExporterPath.KeyCsTemplateSavePath);
            }
            GUILayout.EndHorizontal();
            if (string.IsNullOrEmpty(ExporterTool.ExcelPath) || string.IsNullOrEmpty(ExporterTool.ByteDataPath) || string.IsNullOrEmpty(ExporterTool.CsScriptPath))
            {
                EditorGUILayout.HelpBox("路径未配置.", MessageType.Warning);
                return;
            }
            this.DrawCommonItem();
        }
        
        private void DrawCommonItem()
        {
            if (this.m_LoadError.Length > 0)
            {
                EditorGUILayout.HelpBox(this.m_LoadError.ToString(), MessageType.Error);
            }
            EditorGUILayout.BeginHorizontal(ExcelExporterStyle.ExcelItemHeader);
            if (EditorGUILayout.DropdownButton(new GUIContent(""), FocusType.Passive,ExcelExporterStyle.ExcelItemHeaderToolBarDropDown, GUILayout.Width(18)))
            {
                GenericMenu menu = new GenericMenu();
                menu.AddItem(new GUIContent("全选"),false, () =>
                {
                    foreach (var excel in ExporterTool.AllExcel) excel.Select = true;
                });
                menu.AddItem(new GUIContent("取消所有选择"),false, () =>
                {
                    foreach (var excel in ExporterTool.AllExcel) excel.Select = false;
                });
                menu.AddItem(new GUIContent("反选"),false, () =>
                {
                    foreach (var excel in ExporterTool.AllExcel) excel.Select = !excel.Select;
                });
                menu.AddItem(new GUIContent("选择内容变更"),false, () =>
                {
                    foreach (var excel in ExporterTool.AllExcel) excel.Select = excel.Type == ExcelType.Modify;
                });
                menu.AddItem(new GUIContent("选择新增"),false, () =>
                {
                    foreach (var excel in ExporterTool.AllExcel) excel.Select = excel.Type == ExcelType.New;
                });
                menu.AddItem(new GUIContent("选择新增和内容变更"),false, () =>
                {
                    foreach (var excel in ExporterTool.AllExcel) excel.Select = excel.Type == ExcelType.New || excel.Type == ExcelType.Modify;
                });
                menu.ShowAsContext();
            }
            GUILayout.Space(6);
            GUILayout.Label("状态", ExcelExporterStyle.ExcelItemHeaderToolBarLabel, GUILayout.Width(60));
            GUILayout.Space(4);
            GUILayout.Label("表名", ExcelExporterStyle.ExcelItemHeaderToolBarLabel);
            if (this.m_ExporterType == ExporterType.Cs)
            {
                ExporterTool.HashConfig.IsGenerateByte =
                    GUILayout.Toggle(ExporterTool.HashConfig.IsGenerateByte, "BYTE", GUILayout.Width(80));
                ExporterTool.HashConfig.IsGenerateText =
                    GUILayout.Toggle(ExporterTool.HashConfig.IsGenerateText, "TXT", GUILayout.Width(80));
            }
            if (GUILayout.Button("导出配置", ExcelExporterStyle.Button, GUILayout.Width(160)))
            {
                if (this.m_ExporterType == ExporterType.Lua)
                {
                    ExporterTool.ExporterToLua();
                }
                else if (this.m_ExporterType == ExporterType.Cs)
                {
                    ExporterTool.ExportToCs();
                }
            }
            EditorGUILayout.EndHorizontal();
            
            this.m_ScrollPos = EditorGUILayout.BeginScrollView(this.m_ScrollPos, ExcelExporterStyle.ExcelItemContainer);
            if (ExporterTool.AllExcel.Count <= 0) EditorGUILayout.HelpBox("当前路径下没有Excel表.", MessageType.Info);
            foreach (var item in ExporterTool.AllExcel)
            {
                EditorGUILayout.BeginHorizontal(ExcelExporterStyle.ExcelItem);
                item.Select = EditorGUILayout.Toggle(item.Select, GUILayout.Width(18));
                var color = GUI.contentColor;
                switch (item.Type)
                {
                    case ExcelType.Normal:
                        GUI.contentColor = new Color(0.43f, 0.4f, 0.4f);
                        GUILayout.Label("无变化", GUILayout.Width(60));
                        break;
                    case ExcelType.Modify:
                        GUI.contentColor = new Color(1f, 0.35f, 0f);
                        GUILayout.Label("内容变更", GUILayout.Width(60));
                        break;
                    case ExcelType.New:
                        GUI.contentColor = Color.green;
                        GUILayout.Label("新增", GUILayout.Width(60));
                        break;
                }
                GUI.contentColor = color;
                GUILayout.Label(item.Name);
                EditorGUILayout.EndHorizontal();
                EditorGUILayout.Space(6);
            }
            EditorGUILayout.EndScrollView();
        }

        private void SelectLuaExporter(MouseDownEvent e)
        {
            this.m_LuaExporter.style.backgroundColor = ExcelExporterStyle.Selected;
            this.m_CsExporter.style.backgroundColor = ExcelExporterStyle.Normal;
            if (this.m_ExporterType != ExporterType.Lua)
            {
                this.m_ExporterType = ExporterType.Lua;
                this.InitData();
            }
        }

        private void SelectCsExporter(MouseDownEvent e)
        {
            this.m_LuaExporter.style.backgroundColor = ExcelExporterStyle.Normal;
            this.m_CsExporter.style.backgroundColor = ExcelExporterStyle.Selected;
            if (this.m_ExporterType != ExporterType.Cs)
            {
                this.m_ExporterType = ExporterType.Cs;
                this.InitData();
            }
        }

        private void OnDestroy()
        {
            ExporterTool.AllExcel.Clear();
        }
    }
}